/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package soundscovery;

/**
 *
 * @author Administrator
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.filter.GraphDistanceFilter;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.DragControl;
import prefuse.controls.FocusControl;
import prefuse.controls.NeighborHighlightControl;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Schema;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.io.GraphMLReader;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.GraphLib;
import prefuse.util.GraphicsLib;
import prefuse.util.display.DisplayLib;
import prefuse.util.display.ItemBoundsListener;
import prefuse.util.force.ForceSimulator;
import prefuse.util.io.IOLib;
import prefuse.util.ui.JForcePanel;
import prefuse.util.ui.JValueSlider;
import prefuse.util.ui.UILib;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;



/**
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class UITry3 extends JPanel {

	private static final String graph = "graph";
	private static final String nodes = "graph.nodes";
	private static final String edges = "graph.edges";
	public static UITry3 singleInstance = null;

	private Visualization m_vis;
	static JList jpallete;
	static Vector<String> pallete=new Vector<String>();
	
	static void clearPallete()
	{
		pallete.clear();

		jpallete.setListData(pallete);
		
	}
	
	static void addSoundToPallete(String snd)
	{
		if(snd.length()<1)
		{
			return;
		}
		
		for(int i=0;i<pallete.size();i++)
		{
			if(pallete.get(i).equals(snd))
			{
				return;
			}
		}
		
		pallete.add(snd);
		jpallete.setListData(pallete);
		
	}
	
	public static Graph getGrid(int m, int n) {
		Graph g = new Graph();
		g.getNodeTable().addColumns(new Schema());
		Node[] nodes = new Node[m * n];
		for (int i = 0; i < m * n; ++i) {
			nodes[i] = g.addNode();
			nodes[i].setString("label", String.valueOf(i));

			if (i >= n)
				g.addEdge(nodes[i - n], nodes[i]);
			if (i % n != 0)
				g.addEdge(nodes[i - 1], nodes[i]);
		}
		return g;
	}

	public UITry3(Graph g, String label) {
		super(new BorderLayout());

		// create a new, empty visualization for our data
		m_vis = new Visualization();

		// --------------------------------------------------------------------
		// set up the renderers

		LabelRenderer tr = new LabelRenderer();
		tr.setRoundedCorner(8, 8);
		m_vis.setRendererFactory(new DefaultRendererFactory(tr));

		// --------------------------------------------------------------------
		// register the data with a visualization

		// adds graph to visualization and sets renderer label field
		setGraph(g, label);

		// fix selected focus nodes
		TupleSet focusGroup = m_vis.getGroup(Visualization.FOCUS_ITEMS);
		focusGroup.addTupleSetListener(new TupleSetListener() {
			public void tupleSetChanged(TupleSet ts, Tuple[] add, Tuple[] rem) {
				for (int i = 0; i < rem.length; ++i)
					((VisualItem) rem[i]).setFixed(false);
				for (int i = 0; i < add.length; ++i) {
					((VisualItem) add[i]).setFixed(false);
					((VisualItem) add[i]).setFixed(true);
				}
				if (ts.getTupleCount() == 0) {
					ts.addTuple(rem[0]);
					((VisualItem) rem[0]).setFixed(false);
				}
				m_vis.run("draw");
			}
		});

		// --------------------------------------------------------------------
		// create actions to process the visual data

		int hops = 30;
		final GraphDistanceFilter filter = new GraphDistanceFilter(graph, hops);

		ColorAction fill = new ColorAction(nodes, VisualItem.FILLCOLOR,
				ColorLib.rgb(200, 200, 255));
		fill.add(VisualItem.FIXED, ColorLib.rgb(255, 100, 100));
		fill.add(VisualItem.HIGHLIGHT, ColorLib.rgb(255, 200, 125));

		ActionList draw = new ActionList();
		draw.add(filter);
		draw.add(fill);
		draw.add(new ColorAction(nodes, VisualItem.STROKECOLOR, 0));
		draw.add(new ColorAction(nodes, VisualItem.TEXTCOLOR, ColorLib.rgb(0,
				0, 0)));
		draw.add(new ColorAction(edges, VisualItem.FILLCOLOR, ColorLib
				.gray(200)));
		draw.add(new ColorAction(edges, VisualItem.STROKECOLOR, ColorLib
				.gray(200)));

		ActionList animate = new ActionList(Activity.INFINITY);
		animate.add(new ForceDirectedLayout(graph));
		animate.add(fill);
		animate.add(new RepaintAction());

		// finally, we register our ActionList with the Visualization.
		// we can later execute our Actions by invoking a method on our
		// Visualization, using the name we've chosen below.
		m_vis.putAction("draw", draw);
		m_vis.putAction("layout", animate);

		m_vis.runAfter("draw", "layout");

		// --------------------------------------------------------------------
		// set up a display to show the visualization

		Display display = new Display(m_vis);
		display.setSize(700, 700);
		display.pan(350, 350);
		display.setForeground(Color.GRAY);
		display.setBackground(Color.WHITE);

		// main display controls
		// //display.addControlListener(new FocusControl(1));
		display.addControlListener(new NewControl(1));

		display.addControlListener(new DragControl());
		display.addControlListener(new PanControl());
		display.addControlListener(new ZoomControl());
		display.addControlListener(new WheelZoomControl());
		display.addControlListener(new ZoomToFitControl());
		display.addControlListener(new NeighborHighlightControl());

		// overview display
		// Display overview = new Display(vis);
		// overview.setSize(290,290);
		// overview.addItemBoundsListener(new FitOverviewListener());

		display.setForeground(Color.GRAY);
		display.setBackground(Color.WHITE);

		// --------------------------------------------------------------------
		// launch the visualization

		// create a panel for editing force values
		ForceSimulator fsim = ((ForceDirectedLayout) animate.get(0))
				.getForceSimulator();
		
		//////JForcePanel fpanel = new JForcePanel(fsim);
		JPanel j = new JPanel();
		j.setLayout(new BoxLayout(j, BoxLayout.Y_AXIS));
		JButton b = new JButton("Clear Sound Pallet");
		String[] s = {"Click sound to add it here","","","","","","",""};
		JButton b2 = new JButton("Save sounds in a folder");
		b.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {

				clearPallete();
				
			}
		});
		b2.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				Launch.saveSounds(pallete);				
			}
		});
		
		jpallete = new JList(s);
		
		jpallete.addListSelectionListener(new ListSelectionListener() {
			
			@Override
			public void valueChanged(ListSelectionEvent e) {
				Launch.playSound(jpallete.getSelectedValue().toString() );				
			}
		});
		
		JScrollPane sp = new JScrollPane(jpallete);		
		
		j.add(b);
		j.add(sp);
		j.add(b2);
		
		

		// JPanel opanel = new JPanel();
		// opanel.setBorder(BorderFactory.createTitledBorder("Overview"));
		// opanel.setBackground(Color.WHITE);
		// opanel.add(overview);

		final JValueSlider slider = new JValueSlider("Distance", 0, hops, hops);
		slider.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				filter.setDistance(slider.getValue().intValue());
				m_vis.run("draw");
			}
		});
		slider.setBackground(Color.WHITE);
		slider.setPreferredSize(new Dimension(300, 30));
		slider.setMaximumSize(new Dimension(300, 30));

		Box cf = new Box(BoxLayout.Y_AXIS);
		cf.add(slider);
		cf.setBorder(BorderFactory.createTitledBorder("Connectivity Filter"));
/////		fpanel.add(cf);

		// fpanel.add(opanel);

		/////fpanel.add(Box.createVerticalGlue());

		// create a new JSplitPane to present the interface
		JSplitPane split = new JSplitPane();
		split.setLeftComponent(display);
		/////split.setRightComponent(fpanel);
		split.setRightComponent(j);
		split.setOneTouchExpandable(true);
		split.setContinuousLayout(false);
		split.setDividerLocation(700);

		// now we run our action list
		m_vis.run("draw");

		add(split);
	}

	public static Vector<Vector<Sound>> clusterss;
	public static HashMap<String, Integer> sndcl;
	public static Graph graphs;

	public static void setG(Vector<Vector<Sound>> clusters, int width,
			int height) {
		sndcl = new HashMap<String, Integer>();
		clusterss = clusters;
		Graph b = GraphLib.getGrid(width, height);
		graphs = b;
		for (int i = 0; i < width; i++) {
			for (int j = 0; j < height; j++) {
				if (clusters.get(i + j * width).size() > 2) {
					// b.getNode(i+j*width).setString("label", new
					// String("Populated"));

					/*
					 * for(int c=0;c<clusters.get(i+j*width).size();c++) { Node
					 * n = b.addNode(); n.setString("label", new
					 * String(clusters.
					 * get(i+j*width).get(c).filename.substring(0,
					 * clusters.get(i+j*width).get(c).filename.length()-4)));
					 * b.addEdge(b.getNode(i+j*width), n); }
					 */
					int numSounds = clusters.get(i + j * width).size();
					b.getNode(i + j * width).setString(
							"label",
							new String(
									clusters.get(i + j * width).get(0).filename
											.substring(0,
													clusters.get(i + j * width)
															.get(0).filename
															.length() - 4)
											+ "(" + numSounds + ")"));
					String key = b.getNode(i + j * width).getString("label");// sndcl.get(key);)
					// b.getNode(i+j*width).setString("cindex",new
					// String(""+i+j*width));
					sndcl.put(key, new Integer(i + j * width));

				} else if (clusters.get(i + j * width).size() > 0) {
					b.getNode(i + j * width).setString(
							"label",
							new String(
									clusters.get(i + j * width).get(0).filename
											.substring(0,
													clusters.get(i + j * width)
															.get(0).filename
															.length() - 4)));
				} else {

					b.getNode(i + j * width).setString("label", new String(""));// new
					// String((i)+"."+(j)));

				}

			}
		}

		singleInstance.setGraph(b, "label");
	}

	public void setGraph(Graph g, String label) {
		// update labeling
		DefaultRendererFactory drf = (DefaultRendererFactory) m_vis
				.getRendererFactory();
		((LabelRenderer) drf.getDefaultRenderer()).setTextField(label);

		// update graph
		m_vis.removeGroup(graph);
		VisualGraph vg = m_vis.addGraph(graph, g);
		m_vis.setValue(edges, null, VisualItem.INTERACTIVE, Boolean.FALSE);
		VisualItem f = (VisualItem) vg.getNode(0);
		m_vis.getGroup(Visualization.FOCUS_ITEMS).setTuple(f);
		f.setFixed(false);
	}

	// ------------------------------------------------------------------------
	// Main and demo methods

	public static void main(String[] args) {
		UILib.setPlatformLookAndFeel();

		// create UITry2
		String datafile = null;
		String label = "label";
		if (args.length > 1) {
			datafile = args[0];
			label = args[1];
		}

		JFrame frame = demo(datafile, label);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	public static JFrame demo() {
		return demo((String) null, "label");
	}

	public static JFrame demo(String datafile, String label) {
		Graph g = null;
		if (datafile == null) {
			g = GraphLib.getGrid(15, 15);
			label = "label";
		} else {
			try {
				g = new GraphMLReader().readGraph(datafile);
			} catch (Exception e) {
				e.printStackTrace();
				System.exit(1);
			}
		}

		return demo(g, label);
	}

	public static JFrame demo(Graph g, String label) {
		final UITry3 view = new UITry3(g, label);
		singleInstance = view;

		// set up menu
		JMenu dataMenu = new JMenu("Data");
		dataMenu.add(new OpenGraphAction(view));

		dataMenu.add(new GraphMenuAction("Grid", "ctrl 0", view) {
			protected Graph getGraph() {
				Graph b = GraphLib.getGrid(5, 5);
				b.getNode(0).setString("label", new String("Boogers"));
				return b;
			}
		});

		dataMenu.add(new GraphMenuAction("Grid", "ctrl 1", view) {
			protected Graph getGraph() {
				return GraphLib.getGrid(15, 15);
			}
		});
		dataMenu.add(new GraphMenuAction("Clique", "ctrl 2", view) {
			protected Graph getGraph() {
				return GraphLib.getClique(10);
			}
		});
		dataMenu.add(new GraphMenuAction("Honeycomb", "ctrl 3", view) {
			protected Graph getGraph() {
				return GraphLib.getHoneycomb(5);
			}
		});
		dataMenu.add(new GraphMenuAction("Balanced Tree", "ctrl 4", view) {
			protected Graph getGraph() {
				return GraphLib.getBalancedTree(3, 5);
			}
		});
		dataMenu.add(new GraphMenuAction("Diamond Tree", "ctrl 5", view) {
			protected Graph getGraph() {
				return GraphLib.getDiamondTree(3, 3, 3);
			}
		});
		JMenuBar menubar = new JMenuBar();
		menubar.add(dataMenu);

		// launch window
		JFrame frame = new JFrame("Soundscovery Exporer");
		frame.setJMenuBar(menubar);
		frame.setContentPane(view);
		frame.pack();
		frame.setVisible(true);

		frame.addWindowListener(new WindowAdapter() {
			public void windowActivated(WindowEvent e) {
				view.m_vis.run("layout");
			}

			public void windowDeactivated(WindowEvent e) {
				view.m_vis.cancel("layout");
			}
		});

		return frame;
	}

	// ------------------------------------------------------------------------

	/**
	 * Swing menu action that loads a graph into the graph viewer.
	 */
	public abstract static class GraphMenuAction extends AbstractAction {
		private UITry3 m_view;

		public GraphMenuAction(String name, String accel, UITry3 view) {
			m_view = view;
			this.putValue(AbstractAction.NAME, name);
			this.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
					.getKeyStroke(accel));
		}

		public void actionPerformed(ActionEvent e) {
			m_view.setGraph(getGraph(), "label");
		}

		protected abstract Graph getGraph();
	}

	public static class OpenGraphAction extends AbstractAction {
		private UITry3 m_view;

		public OpenGraphAction(UITry3 view) {
			m_view = view;
			this.putValue(AbstractAction.NAME, "Open File...");
			this.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
					.getKeyStroke("ctrl O"));
		}

		public void actionPerformed(ActionEvent e) {
			Graph g = IOLib.getGraphFile(m_view);
			if (g == null)
				return;
			String label = getLabel(m_view, g);
			if (label != null) {
				m_view.setGraph(g, label);
			}
		}

		public static String getLabel(Component c, Graph g) {
			// get the column names
			Table t = g.getNodeTable();
			int cc = t.getColumnCount();
			String[] names = new String[cc];
			for (int i = 0; i < cc; ++i)
				names[i] = t.getColumnName(i);

			// where to store the result
			final String[] label = new String[1];

			// -- build the dialog -----
			// we need to get the enclosing frame first
			while (c != null && !(c instanceof JFrame)) {
				c = c.getParent();
			}
			final JDialog dialog = new JDialog((JFrame) c,
					"Choose Label Field", true);

			// create the ok/cancel buttons
			final JButton ok = new JButton("OK");
			ok.setEnabled(false);
			ok.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					dialog.setVisible(false);
				}
			});
			JButton cancel = new JButton("Cancel");
			cancel.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					label[0] = null;
					dialog.setVisible(false);
				}
			});

			// build the selection list
			final JList list = new JList(names);
			list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			list.getSelectionModel().addListSelectionListener(
					new ListSelectionListener() {
						public void valueChanged(ListSelectionEvent e) {
							int sel = list.getSelectedIndex();
							if (sel >= 0) {
								ok.setEnabled(true);
								label[0] = (String) list.getModel()
										.getElementAt(sel);
							} else {
								ok.setEnabled(false);
								label[0] = null;
							}
						}
					});
			JScrollPane scrollList = new JScrollPane(list);

			JLabel title = new JLabel("Choose a field to use for node labels:");

			// layout the buttons
			Box bbox = new Box(BoxLayout.X_AXIS);
			bbox.add(Box.createHorizontalStrut(5));
			bbox.add(Box.createHorizontalGlue());
			bbox.add(ok);
			bbox.add(Box.createHorizontalStrut(5));
			bbox.add(cancel);
			bbox.add(Box.createHorizontalStrut(5));

			// put everything into a panel
			JPanel panel = new JPanel(new BorderLayout());
			panel.add(title, BorderLayout.NORTH);
			panel.add(scrollList, BorderLayout.CENTER);
			panel.add(bbox, BorderLayout.SOUTH);
			panel.setBorder(BorderFactory.createEmptyBorder(5, 2, 2, 2));

			// show the dialog
			dialog.setContentPane(panel);
			dialog.pack();
			dialog.setLocationRelativeTo(c);
			dialog.setVisible(true);
			dialog.dispose();

			// return the label field selection
			return label[0];
		}
	}

	public static class NewControl extends FocusControl {
		NewControl() {
			super();
		}

		NewControl(int i) {
			super(i);
		}

		static HashMap<String, Integer> flower;
		static int lastindex = -1;
		static String lastsoundName = "";
		static Vector<Node> lastnodes = new Vector<Node>();

		public void expandNode(int index) {
			graphs.getNode(index).setString("label", "");
			
			if(lastindex != -1)
			{

				graphs.getNode(lastindex).setString("label", lastsoundName );
				//remove the stuff
				for(int i=0; i<lastnodes.size();i++)
				{
					graphs.removeNode(lastnodes.get(i));
				}
			}
			
			lastindex = index;
			lastsoundName=clusterss.get(index).get(0).filename.substring(0, clusterss.get(index).get(0).filename.length() - 4)
				+"("+clusterss.get(index).size() +")";
			lastnodes.clear();

			Graph b = graphs;			
			for (int c = 0; c < clusterss.get(index).size(); c++) {
				Node n = b.addNode();
				n.setString("label", new String(clusterss.get(index)
						.get(c).filename.substring(0, clusterss.get(
								index).get(c).filename.length() - 4)));
				b.addEdge(b.getNode(index), n);
				lastnodes.add(n);
			}

		}

		@Override
		public void itemClicked(VisualItem vi, MouseEvent me) {
			super.itemClicked(vi, me);
			System.out.println("clicked " + vi.getString("label"));
			if (vi.getString("label").length() > 0) {
				String l = vi.getString("label");
				if (l.charAt(l.length() - 1) == ')') {
					System.out.println("Its a trap at " + sndcl.get(l) + "!");
					expandNode(sndcl.get(l));					
				}
				else
				{
					addSoundToPallete(vi.getString("label"));
				}
			}
			
			
		}

		@Override
		public void itemEntered(VisualItem item, MouseEvent e) {
			if(item==null)
				return;
			super.itemEntered(item, e);
			try
			{
			if (item.getString("label") != null && item.getString("label").length() > 0) {
				// play sound
				String l = item.getString("label");
				if (l.charAt(l.length() - 1) == ')') {

					System.out.println("Its a trap at " + sndcl.get(l) + "!");

				}
				while (l.indexOf("(") != -1) {
					l = l.substring(0, l.length() - 1);
				}

				Launch.playSound(l);
			}
			}
			catch (ArrayIndexOutOfBoundsException e1)
			{
				//not sure why this happens but it does not break anything...
			}
		}

	}

	public static class FitOverviewListener implements ItemBoundsListener {
		private Rectangle2D m_bounds = new Rectangle2D.Double();
		private Rectangle2D m_temp = new Rectangle2D.Double();
		private double m_d = 15;

		public void itemBoundsChanged(Display d) {
			d.getItemBounds(m_temp);
			GraphicsLib.expand(m_temp, 25 / d.getScale());

			double dd = m_d / d.getScale();
			double xd = Math.abs(m_temp.getMinX() - m_bounds.getMinX());
			double yd = Math.abs(m_temp.getMinY() - m_bounds.getMinY());
			double wd = Math.abs(m_temp.getWidth() - m_bounds.getWidth());
			double hd = Math.abs(m_temp.getHeight() - m_bounds.getHeight());
			if (xd > dd || yd > dd || wd > dd || hd > dd) {
				m_bounds.setFrame(m_temp);
				DisplayLib.fitViewToBounds(d, m_bounds, 0);
			}
		}
	}

} // end of class UITry2

